<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
  
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  
  <link href="../css/style.css" rel="stylesheet" type="text/css">

</head>
<body>
<h1>4.12. Линейный сдвиг</h1>
<p class="article">
Сдвиги — это особые операции процессора, которые позволяют реализовать различные преобразования данных, работать с отдельными битами, а также быстро выполнять умножение и деление чисел на степень 2. В этой части мы рассмотрим операции линейного сдвига, а в следующей будут циклические.
</p>
<h2>Логический сдвиг вправо</h2>
<p class="article">
Логический сдвиг всегда выполняется без учёта знакового бита. Для логического сдвига вправо предназначена команда SHR. У этой команды два операнда. Первый операнд представляет собой сдвигаемое значение и на его место записывается результат операции. Второй операнд указывает, на сколько бит нужно осуществить сдвиг. Этим операндом может быть либо непосредственное значение, либо регистр CL. Схема выполнения операции показана на рисунке:
</p>
<div class="image">
	<img src="../img/4.12_1.png" />
	<p>Состояние ячеек при логическом сдвиге вправо</p>
</div>
<p class="article">
Все биты операнда сдвигаются вправо (от старших битов к младшим). Выдвинутый бит становится значением флага CF. Старший бит получает нулевое значение. Эта операция повторяется несколько раз, если второй операнд больше единицы. Логический сдвиг вправо можно использовать для деления целых чисел без знака на степень 2, причём сдвиг работает быстрее, чем команда деления DIV. <br /> Примеры:
</p>
<pre class="code">
    shr ax,1         ;Логический сдвиг AX на 1 бит вправо
    shr byte[bx],cl  ;Лог. сдвиг байта по адресу BX на СL бит вправо
    shr cl,4         ;CL = CL / 16 (для числа без знака)
</pre>
<h2>Арифметический сдвиг вправо</h2>
<p class="article">
Арифметический сдвиг отличается от логического тем, что он не изменяет значение старшего бита, и предназначен для чисел со знаком. Арифметический сдвиг вправо выполняется командой SAR. У этой команды тоже 2 операнда, аналогично команде SHR. Схема выполнения операции показана на рисунке:
</p>
<div class="image">
	<img src="../img/4.12_2.png" />
	<p>Состояние ячеек при арифметическом сдвиге вправо</p>
</div>
<p class="article">
Выдвинутый бит становится значением флага CF. Знаковый бит не изменяется. При сдвиге на 1 бит сбрасывается флаг OF. Эту команду можно использовать для деления целых чисел со знаком на степень 2 (обратите внимание, что «округление» всегда в сторону меньшего числа, поэтому для отрицательных чисел результат будет отличаться от результата деления с помощью команды IDIV). Примеры:
</p>
<pre class="code">
    sar bx,1         ;Арифметический сдвиг BX на 1 бит вправо
    sar di,cl        ;Арифметический сдвиг DI на CL бит вправо
    sar [x],3        ;x = x / 8 (x - 8-битное значение со знаком)
</pre>
<h2>Логический и арифметический сдвиг влево</h2>
<p class="article">
Логический сдвиг влево выполняется командой SHL, а арифметический — командой SAL. Однако, на самом деле это просто синонимы для одной и той же машинной команды. Сдвиг влево одинаков для чисел со знаком и чисел без знака. У команды 2 операнда, аналогично командам SHR и SAR.Схема этой операции показана на рисунке:
</p>
<div class="image">
	<img src="../img/4.12_3.png" />
	<p>Состояние ячеек при сдвиге влево</p>
</div>
<p class="article">
Старший бит становится значением флага CF, а младший получает нулевое значение. С помощью сдвига влево можно быстро умножать числа на степень 2. Но будьте внимательны, чтобы не получить в результате переполнение. Если при сдвиге на 1 бит меняется значение старшего бита, то устанавливается флаг OF. Примеры использования команды:
</p>
<pre class="code">
    shl dx,1         ;Сдвиг DX на 1 бит влево
    sal dx,1         ;То же самое
    shl ax,cl        ;Сдвиг AX на CL бит влево
    sal [x],2        ;x = x * 4
</pre>
<h2>Сдвиги двойной точности</h2>
<p class="article">
Существуют ещё две команды, осуществляющие более сложные сдвиги. SHRD — сдвиг двойной точности вправо, SHLD — сдвиг двойной точности влево. У этих команд 3 операнда. Первый операнд — сдвигаемое значение и место для записи результата, должен иметь размер 16 бит. Второй операнд — источник вдвигаемых битов, тоже должен иметь размер 16 бит и находится в одном из регистров. Значение второго операнда не меняется. Третий операнд — счётчик сдвигов, может быть непосредственным значением или находиться в регистре CL. Схемы работы этих команд показаны на рисунке:
</p>
<div class="image">
	<img src="../img/4.12_4.png" />
	<p>Принцип сдвига двойной точности</p>
</div>
<p class="article">
Пример
</p>
<pre class="code">
    shld ax,bx,3     ;Сдвинуть ax на 3 бита влево,
                     ;3 старших бита BX становятся младшими битами AX
</pre>
<h2>Пример программы</h2>
<p class="article">
Программа печатает переменную размером 16-бит в двоичном виде. Используется команда сдвига влево на 1 бит, после чего анализируется значение флага CF. Если CF=0, выводим символ ’0′, если СF=1 выводим символ ’1′. Проверка битов осуществляется в цикле.
</p>
<pre class="code">
use16                ;Генерировать 16-битный код
org 100h             ;Программа начинается с адреса 100h
    jmp start        ;Безусловный переход на метку start
;-- Данные ------------------------------------------------------------
v   dw 12345
pak db 13,10,'Press any key...$'
;----------------------------------------------------------------------
start:
    mov bx,[v]       ;BX = v
    mov ah,2         ;Функция DOS 02h - вывод символа
    mov cx,16        ;Инициализация счётчика цикла
lp:
    shl bx,1         ;Сдвиг BX на 1 бит влево
    mov dl,'0'       ;dl = '0'
    jnc print        ;Переход, если выдвинутый бит равен 0
    inc dl           ;dl = dl + 1 = '1'
print:
    int 21h          ;Обращение к функции DOS 02h
    loop lp          ;Команда цикла
 
    mov ah,9         ;\
    mov dx,pak       ; > Вывод строки 'Press any key...'
    int 21h          ;/
 
    mov ah,8         ;\
    int 21h          ;/ Ввод символа без эха
 
    mov ax,4C00h     ;\
    int 21h          ;/ Завершение программы
</pre>
</body>
</html>
